Draft: prove registry-first WoT Binding and Connectivity 1.1 stack - #19
Closed
marcschier wants to merge 20 commits into
Closed
Draft: prove registry-first WoT Binding and Connectivity 1.1 stack#19marcschier wants to merge 20 commits into
marcschier wants to merge 20 commits into
Conversation
Merged
8 tasks
…ver limits - Make uav:nodeSet.sha256 mandatory: a missing, malformed, or mismatched digest now produces a structured InvalidDigest/ DigestMismatch diagnostic and no NodeSet is returned, instead of silently skipping integrity verification when the field was absent. - Catch XML/deserialization failures from UANodeSet.Read and report them as a WotDiagnosticCode.MalformedNodeSet diagnostic instead of letting the exception escape the converter. - Create exactly one WotResolutionContext per top-level conversion in WotNodeSetConverter.ToNodeSetCore, seeded from the converter options (depth, max documents, cumulative bytes), and thread that single context through link/thing resolution. Removes the previous per-link context ??= new WotResolutionContext() fallback in TryResolveTargetNodeId, which reset cycle/limit tracking for every link instead of applying bounds across the whole conversion. - Seed WotNodeSetDocumentConverter's resolution context from its configured options instead of resolver defaults. - Add WotNodeSetConverterOptions.MaxResolverDocuments/ MaxResolverDocumentBytes/MaxResolverTotalBytes (aligned with WotResolverOptions defaults) plus ToResolverOptions() and validation, so aggregate resolver limits are configurable through the same options object used for the rest of the conversion. - Add tests: missing/malformed digest, digest mismatch, malformed NodeSet XML without throwing, unsupported vs. accepted (base64) encoding, multi-link aggregate document/byte limits sharing one context, and sibling-link cycle detection that doesn't block unrelated links. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After a ShadowReload the retired generation is removed from the routing table but still owns the monitored items created on it. Modify, Delete, SetMonitoringMode and Transfer previously iterated only the visible routing-table managers, so a same-namespace replacement generation would claim (and fail on) the retired items - returning BadMonitoredItemIdInvalid and leaving the retired generation's items undrained and its address space never disposed until another lifecycle operation or shutdown. Dispatch each data monitored item to its recorded owning NodeManager (grouped by owner, each owner offered only its own indices) so a retired generation services its own items and a same-namespace replacement cannot cross-claim them. Event/all-event handling and result indexing are unchanged. Trigger prompt retired-generation cleanup when the last item drains: the master notifies the lifecycle from the Delete path when an owner is no longer registered, and the lifecycle runs cleanup on a background task (request execution context suppressed, coordinated via the lifecycle semaphore) so nothing is disposed inside the request callback. Existing fail-closed Reload/Remove behaviour and the deferred-cleanup fallback are preserved. Add tests proving that after ShadowReload old data items can be modified, disabled/re-enabled, transferred and deleted on the retired generation, new reads use the replacement, old notifications keep flowing, and the retired generation is disposed after the final drain without another lifecycle operation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rects) MQTT: mqtts now enables TLS and defaults to port 8883, resolving username / password, TLS client certificate and trust anchors through the credential provider. A declared security scheme fails closed when unresolved, and username / password never downgrades to plaintext unless explicitly allowed. plain mqtt stays explicit (port 1883). Modbus: the planner enforces address 0-65535 and the address+quantity-1 range, maps function-only forms exactly onto codes 1,2,3,4,5,6,15,16 (mnemonic or numeric) and rejects op/function and entity/function mismatches. The executor re-validates the range before the ushort/byte casts. HTTP: the executor-owned HttpClient disables automatic redirects and applies a bounded, origin-aware redirect policy that strips custom header/query credentials across origins, refuses loops, non-http(s) schemes and https->http downgrades, and caps hops. A caller-supplied client with a credential-bearing form fails closed unless CallerClientHandlesRedirectSafety is set. Adds planner, executor and E2E regression tests and updates the binding docs and options. WotCredential gains runtime-only client certificate / trust material. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Registry mutations now persist through a transactional store commit before publishing the snapshot or raising Changed, and the generated WoT V2 event types are fully populated from the coordinator arguments. - IWotRegistryStore: replace the per-entity Upsert/Remove methods with a single atomic CommitAsync(snapshot). LoadAsync returns only the committed generation. - FileWotRegistryStore: stage content-addressed version blobs durably, then switch a single manifest.json pointer with an atomic replace, so a crash never exposes a half-written generation; Load reads only the committed manifest. - InMemoryWotRegistryStore: keep the committed snapshot behind an atomic reference switch (same failure semantics). - WotRegistryService: commit durably before publishing m_snapshot or raising Changed for every CRUD/default/enabled/labels and projection path. On failure Current stays previous, no event is raised, a retry re-attempts persistence and a restart never sees partial data. - WotRegistryNodeManager: populate every generated WoT event field (resource identity/kind, generation, phase/outcome, validation outcome, load state/failed node/reason, binding uri, refresh summary/request id) from WotMaterializationEventArgs via SetChildValue. - Tests: fault-injection transaction tests (atomicity, no-event, retry, restart) and real EventFilter integration tests that verify the typed fields are delivered through the running server notifier chain. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Regenerate the Opc.Ua.WotCon model from the single revised Opc.Ua.WoTCon NodeSet2, which incorporates the published OPC 10100-1 v1.02 model (NodeIds 1..172, deprecated) plus the additive registry nodes (64000+) in one namespace (http://opcfoundation.org/UA/WoT-Con/, version 1.1.0), replacing the separate 1.02 ModelDesign and the Opc.Ua.WotCon.V2 companion model. Model / build: - Pin Opc.Ua.WoTCon.NodeSet2.xml/.csv (+ xRegistry) and drop the V2-named artifacts; update Sync-WotConModels.ps1 mappings/docs. - Opc.Ua.WotCon.csproj: stop compiling WotConnection.xml/csv; compile the combined NodeSet once as Opc.Ua.WotCon (name WotCon, model URI WoT-Con/). Legacy 1.02 sources kept under Design/ for reference only. Server / tests / docs: - Replace all Opc.Ua.WotCon.V2 namespace/type references with the combined Opc.Ua.WotCon generated types (AddOpcUaWotCon, ObjectIds, BrowseNames, DataTypeIds, ...); remove user-visible V2 wording and rename the registry config section to OpcUa:WotConRegistry:Server. - Decide model-node ownership cleanly via WotConModelPartition: the registry NodeManager owns {WotCon, xRegistry} and loads the registry slice (>=64000); the legacy asset NodeManager loads the incorporated 1.02 slice (<64000). Neither claims the same static node twice. - Add CombinedModelPreservationTests proving exact 1.02 NodeIds, typed method state/results and the generated client API are preserved, and that the registry types now coexist in Opc.Ua.WotCon. Source generator (NodeSet2 -> ModelDesign) fixes exposed by generating the richer combined model with typed method arguments: - Map placeholder BrowseNames "<Name>" to "Name_Placeholder". - Reuse an existing explicit method-type declaration instead of synthesizing a colliding duplicate, and carry the concrete method's arguments onto it. - Register the standard encodeable types (Argument) on the value decoder so NodeSet2 method argument lists actually decode (was silently empty). - Disambiguate a by-ref output argument whose name matches an input. - Skip synthesizing method types for base-namespace (inherited) methods and for standalone method-type declarations. Deprecated 1.02 nodes remain usable and all 1.02 tests pass unchanged (only mechanical Ua.DataTypeIds qualification where the new combined DataTypeIds shadows Core). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Status and dependencies
Draft proof implementation.
[Server] Add live NodeManager lifecycle support) and intentionally targets its head branch.What this proves
Lossless NodeSet2 ↔ WoT conversion
Opc.Ua.Types.Extensions.uav:nodesprojection only when readable-only reverse conversion does not reproduce the complete UANodeSet.WhenRequired,Always, andNeverpreservation modes.uav:nodeSetis emitted only for explicit byte archival or a demonstrated fallback, and completeness tests useNever.WoT source generation
.tm.json,.td.json,.tm.jsonld,.td.jsonldand explicitly opted-in.jsonldAdditionalFiles.UANodeSetand uses the existing direct NodeSet code-generation pipeline.MODELGEN030–MODELGEN034diagnostics for parsing, conversion and virtual-path collisions.Live NodeManager shadow reload
ShadowReloadAsync.ImmediateReloadAsyncas the alternative policy: it atomically routes new requests to the replacement, invalidates affected non-durable monitored items withBadNodeIdUnknown, detaches disposed owner graphs, and rejects durable subscriptions before commit.ReloadAsync/RemoveAsyncbehavior unchanged.Registry-first WoT Connectivity 1.1
WotProjectionRetirementPolicyso deployments select graceful or immediate replacement.Replaceable protocol binding code-behind
Adds a dependency-light planner/binder project for:
Concrete optional executor projects cover HTTP, MQTT, Modbus TCP and OPC UA. The OPC UA executor supports Read, Write, native data-change observation, Method invocation and Event subscription, including portable
nsu=NodeIds.BACnet, PROFINET, LoRaWAN and CoAP are planner/validation-only in this proof and report their non-executable capability explicitly.
Review remediation
The follow-up review fixes are included:
mqttsenables TLS, defaults to 8883, resolves credentials/trust and fails closed;uav:nodeSet.sha256is mandatory and malformed XML becomes structured diagnostics;uav:eventType, persisted identities use portablensu=ExpandedNodeIds, andHasComponentsubtypes round-trip exactly;uaand deterministicnsNprefixes; ReferenceType model names are emitted directly in linkrel, withuav:refIdas definitive identifier, and name/NodeId conflicts are rejected;mapToTypeName/congruentTypeNamehints remain paired with definitive identifiers, while arbitrary instance targets remain ExpandedNodeIds;uav:typedReference/refTypeNamedraft surface is removed;@contextprefixes (with URI-qualified QualifiedNames also accepted), authored IDs are retained during synthesis, and the proof omitsuav:nodeswhen the semantic mapping alone is equivalent;observable/observepropertyis documented as advertised binding support, not a restriction on core UA monitorability.Compatibility
IWotAssetProviderFactoryintegrations remain supported.Opc.Ua.WotCon.Server.Commit structure
Add shadow NodeManager reload supportAdd lossless WoT conversion and source generationAdd injectable WoT protocol bindingsAdd registry-first WoT Connectivity runtimeAlign WoT Connectivity proof to combined 1.1 spec modelAlign WoT converter with portable Binding identityMake WoT NodeSet conversion native-firstAddress WoT model concept review feedbackAddress WoT mapping review feedbackPrefer context prefixes for WoT QualifiedNamesAddress registry resolution and retirement feedbackValidation
Opc.Ua.Types.Testsnet10.0 and net48: passedOpc.Ua.WotCon.TestsandOpc.Ua.WotCon.Binding.Testsnet10.0: passeddotnet build UA.slnx -c Release: passedOpc.Ua.Server/Opc.Ua.WotCon.Serverbuilds on net10.0 and net472: passed with 0 warningsgit diff --check: clean